From 1b63fcf74e4049669ebf4073886086335905afd7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 23 Mar 2018 14:51:32 +0300 Subject: [PATCH] Refactor context to extract dependencies calculation to a separate mod --- src/cargo/ops/cargo_clean.rs | 1 + .../{context.rs => context/mod.rs} | 267 ++-------------- .../cargo_rustc/context/unit_dependencies.rs | 301 ++++++++++++++++++ src/cargo/ops/cargo_rustc/custom_build.rs | 5 +- src/cargo/ops/cargo_rustc/fingerprint.rs | 2 +- src/cargo/ops/cargo_rustc/job_queue.rs | 2 +- src/cargo/ops/cargo_rustc/mod.rs | 7 +- src/cargo/ops/cargo_rustc/output_depinfo.rs | 2 +- 8 files changed, 341 insertions(+), 246 deletions(-) rename src/cargo/ops/cargo_rustc/{context.rs => context/mod.rs} (82%) create mode 100644 src/cargo/ops/cargo_rustc/context/unit_dependencies.rs diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index 89939e02d..7f7d96225 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -100,6 +100,7 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { } cx.probe_target_info()?; + cx.build_unit_dependencies(&units)?; for unit in units.iter() { rm_rf(&cx.fingerprint_dir(unit), config)?; diff --git a/src/cargo/ops/cargo_rustc/context.rs b/src/cargo/ops/cargo_rustc/context/mod.rs similarity index 82% rename from src/cargo/ops/cargo_rustc/context.rs rename to src/cargo/ops/cargo_rustc/context/mod.rs index cdcfe414f..d51984375 100644 --- a/src/cargo/ops/cargo_rustc/context.rs +++ b/src/cargo/ops/cargo_rustc/context/mod.rs @@ -14,7 +14,6 @@ use jobserver::Client; use core::{Package, PackageId, PackageSet, Profile, Resolve, Target}; use core::{Dependency, Profiles, TargetKind, Workspace}; -use core::dependency::Kind as DepKind; use util::{self, internal, profile, Cfg, CfgExpr, Config, ProcessBuilder}; use util::errors::{CargoResult, CargoResultExt}; @@ -25,6 +24,9 @@ use super::layout::Layout; use super::links::Links; use super::{BuildConfig, Compilation, Kind}; +mod unit_dependencies; +use self::unit_dependencies::build_unit_dependencies; + /// All information needed to define a Unit. /// /// A unit is an object that has enough information so that cargo knows how to build it. @@ -102,6 +104,7 @@ pub struct Context<'a, 'cfg: 'a> { profiles: &'a Profiles, incremental_env: Option, + unit_dependencies: Option, Vec>>>, /// For each Unit, a list all files produced as a triple of /// /// - File name that will be produced by the build process (in `deps`) @@ -204,6 +207,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { jobserver, build_script_overridden: HashSet::new(), + unit_dependencies: None, // TODO: Pre-Calculate these with a topo-sort, rather than lazy-calculating target_filenames: HashMap::new(), target_metadatas: HashMap::new(), @@ -232,6 +236,12 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Ok(()) } + pub fn build_unit_dependencies(&mut self, units: &[Unit<'a>]) -> CargoResult<()> { + assert!(self.unit_dependencies.is_none()); + self.unit_dependencies = Some(build_unit_dependencies(units, self)?); + Ok(()) + } + /// Ensure that we've collected all target-specific information to compile /// all the units mentioned in `units`. pub fn probe_target_info(&mut self) -> CargoResult<()> { @@ -361,7 +371,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { if is_plugin { self.used_in_plugin.insert(*unit); } - for unit in self.dep_targets(unit)? { + for unit in self.dep_targets(unit) { self.walk_used_in_plugin_map(&unit, is_plugin || unit.target.for_host(), visited)?; } Ok(()) @@ -526,9 +536,10 @@ impl<'a, 'cfg> Context<'a, 'cfg> { .hash(&mut hasher); // Mix in the target-metadata of all the dependencies of this target - if let Ok(deps) = self.dep_targets(unit) { - let mut deps_metadata = deps.into_iter() - .map(|dep_unit| self.target_metadata(&dep_unit)) + { + let mut deps_metadata = self.dep_targets(unit) + .iter() + .map(|dep_unit| self.target_metadata(dep_unit)) .collect::>(); deps_metadata.sort(); deps_metadata.hash(&mut hasher); @@ -770,236 +781,25 @@ impl<'a, 'cfg> Context<'a, 'cfg> { /// For a package, return all targets which are registered as dependencies /// for that package. - pub fn dep_targets(&self, unit: &Unit<'a>) -> CargoResult>> { - if unit.profile.run_custom_build { - return self.dep_run_custom_build(unit); - } else if unit.profile.doc && !unit.profile.test { - return self.doc_deps(unit); - } - - let id = unit.pkg.package_id(); - let deps = self.resolve.deps(id); - let mut ret = deps.filter(|dep| { - unit.pkg - .dependencies() - .iter() - .filter(|d| d.name() == dep.name() && d.version_req().matches(dep.version())) - .any(|d| { - // If this target is a build command, then we only want build - // dependencies, otherwise we want everything *other than* build - // dependencies. - if unit.target.is_custom_build() != d.is_build() { - return false; - } - - // If this dependency is *not* a transitive dependency, then it - // only applies to test/example targets - if !d.is_transitive() && !unit.target.is_test() && !unit.target.is_example() - && !unit.profile.test - { - return false; - } - - // If this dependency is only available for certain platforms, - // make sure we're only enabling it for that platform. - if !self.dep_platform_activated(d, unit.kind) { - return false; - } - - // If the dependency is optional, then we're only activating it - // if the corresponding feature was activated - if d.is_optional() && !self.resolve.features(id).contains(&*d.name()) { - return false; - } - - // If we've gotten past all that, then this dependency is - // actually used! - true - }) - }).filter_map(|id| match self.get_package(id) { - Ok(pkg) => pkg.targets().iter().find(|t| t.is_lib()).map(|t| { - let unit = Unit { - pkg, - target: t, - profile: self.lib_or_check_profile(unit, t), - kind: unit.kind.for_target(t), - }; - Ok(unit) - }), - Err(e) => Some(Err(e)), - }) - .collect::>>()?; - - // If this target is a build script, then what we've collected so far is - // all we need. If this isn't a build script, then it depends on the - // build script if there is one. - if unit.target.is_custom_build() { - return Ok(ret); - } - ret.extend(self.dep_build_script(unit)); - - // If this target is a binary, test, example, etc, then it depends on - // the library of the same package. The call to `resolve.deps` above - // didn't include `pkg` in the return values, so we need to special case - // it here and see if we need to push `(pkg, pkg_lib_target)`. - if unit.target.is_lib() && !unit.profile.doc { - return Ok(ret); - } - ret.extend(self.maybe_lib(unit)); - - // Integration tests/benchmarks require binaries to be built - if unit.profile.test && (unit.target.is_test() || unit.target.is_bench()) { - ret.extend( - unit.pkg - .targets() - .iter() - .filter(|t| { - let no_required_features = Vec::new(); - - t.is_bin() && - // Skip binaries with required features that have not been selected. - t.required_features().unwrap_or(&no_required_features).iter().all(|f| { - self.resolve.features(id).contains(f) - }) - }) - .map(|t| Unit { - pkg: unit.pkg, - target: t, - profile: self.lib_or_check_profile(unit, t), - kind: unit.kind.for_target(t), - }), - ); - } - Ok(ret) - } - - /// Returns the dependencies needed to run a build script. - /// - /// The `unit` provided must represent an execution of a build script, and - /// the returned set of units must all be run before `unit` is run. - pub fn dep_run_custom_build(&self, unit: &Unit<'a>) -> CargoResult>> { + // TODO: this ideally should be `-> &[Unit<'a>]` + pub fn dep_targets(&self, unit: &Unit<'a>) -> Vec> { // If this build script's execution has been overridden then we don't // actually depend on anything, we've reached the end of the dependency // chain as we've got all the info we're gonna get. - let key = (unit.pkg.package_id().clone(), unit.kind); - if self.build_script_overridden.contains(&key) { - return Ok(Vec::new()); - } - - // When not overridden, then the dependencies to run a build script are: // - // 1. Compiling the build script itself - // 2. For each immediate dependency of our package which has a `links` - // key, the execution of that build script. - let not_custom_build = unit.pkg - .targets() - .iter() - .find(|t| !t.is_custom_build()) - .unwrap(); - let tmp = Unit { - target: not_custom_build, - profile: &self.profiles.dev, - ..*unit - }; - let deps = self.dep_targets(&tmp)?; - Ok(deps.iter() - .filter_map(|unit| { - if !unit.target.linkable() || unit.pkg.manifest().links().is_none() { - return None; - } - self.dep_build_script(unit) - }) - .chain(Some(Unit { - profile: self.build_script_profile(unit.pkg.package_id()), - kind: Kind::Host, // build scripts always compiled for the host - ..*unit - })) - .collect()) - } - - /// Returns the dependencies necessary to document a package - fn doc_deps(&self, unit: &Unit<'a>) -> CargoResult>> { - let deps = self.resolve - .deps(unit.pkg.package_id()) - .filter(|dep| { - unit.pkg - .dependencies() - .iter() - .filter(|d| d.name() == dep.name()) - .any(|dep| match dep.kind() { - DepKind::Normal => self.dep_platform_activated(dep, unit.kind), - _ => false, - }) - }) - .map(|dep| self.get_package(dep)); - - // To document a library, we depend on dependencies actually being - // built. If we're documenting *all* libraries, then we also depend on - // the documentation of the library being built. - let mut ret = Vec::new(); - for dep in deps { - let dep = dep?; - let lib = match dep.targets().iter().find(|t| t.is_lib()) { - Some(lib) => lib, - None => continue, - }; - ret.push(Unit { - pkg: dep, - target: lib, - profile: self.lib_or_check_profile(unit, lib), - kind: unit.kind.for_target(lib), - }); - if self.build_config.doc_all { - ret.push(Unit { - pkg: dep, - target: lib, - profile: &self.profiles.doc, - kind: unit.kind.for_target(lib), - }); + // Note there's a subtlety about this piece of code! The + // `build_script_overridden` map here is populated in + // `custom_build::build_map` which you need to call before inspecting + // dependencies. However, that code itself calls this method and + // gets a full pre-filtered set of dependencies. This is not super + // obvious, and clear, but it does work at the moment. + if unit.profile.run_custom_build { + let key = (unit.pkg.package_id().clone(), unit.kind); + if self.build_script_overridden.contains(&key) { + return Vec::new(); } } - - // Be sure to build/run the build script for documented libraries as - ret.extend(self.dep_build_script(unit)); - - // If we document a binary, we need the library available - if unit.target.is_bin() { - ret.extend(self.maybe_lib(unit)); - } - Ok(ret) - } - - /// If a build script is scheduled to be run for the package specified by - /// `unit`, this function will return the unit to run that build script. - /// - /// Overriding a build script simply means that the running of the build - /// script itself doesn't have any dependencies, so even in that case a unit - /// of work is still returned. `None` is only returned if the package has no - /// build script. - fn dep_build_script(&self, unit: &Unit<'a>) -> Option> { - unit.pkg - .targets() - .iter() - .find(|t| t.is_custom_build()) - .map(|t| Unit { - pkg: unit.pkg, - target: t, - profile: &self.profiles.custom_build, - kind: unit.kind, - }) - } - - fn maybe_lib(&self, unit: &Unit<'a>) -> Option> { - unit.pkg - .targets() - .iter() - .find(|t| t.linkable()) - .map(|t| Unit { - pkg: unit.pkg, - target: t, - profile: self.lib_or_check_profile(unit, t), - kind: unit.kind.for_target(t), - }) + self.unit_dependencies.as_ref().unwrap()[unit].clone() } fn dep_platform_activated(&self, dep: &Dependency, kind: Kind) -> bool { @@ -1066,15 +866,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> { } } - pub fn lib_or_check_profile(&self, unit: &Unit, target: &Target) -> &'a Profile { - if !target.is_custom_build() && !target.for_host() - && (unit.profile.check || (unit.profile.doc && !unit.profile.test)) - { - return &self.profiles.check; - } - self.lib_profile() - } - pub fn build_script_profile(&self, _pkg: &PackageId) -> &'a Profile { // TODO: should build scripts always be built with the same library // profile? How is this controlled at the CLI layer? diff --git a/src/cargo/ops/cargo_rustc/context/unit_dependencies.rs b/src/cargo/ops/cargo_rustc/context/unit_dependencies.rs new file mode 100644 index 000000000..2fe20cde0 --- /dev/null +++ b/src/cargo/ops/cargo_rustc/context/unit_dependencies.rs @@ -0,0 +1,301 @@ +//! Constructs the dependency graph for compilation. +//! +//! Rust code is typically organized as a set of Cargo packages. The +//! dependencies between the packages themselves are stored in the +//! `Resolve` struct. However, we can't use that information as is for +//! compilation! A package typically contains several targets, or crates, +//! and these targets has inter-dependencies. For example, you need to +//! compile the `lib` target before the `bin` one, and you need to compile +//! `build.rs` before either of those. +//! +//! So, we need to lower the `Resolve`, which specifies dependencies between +//! *packages*, to a graph of dependencies between their *targets*, and this +//! is exactly what this module is doing! Well, almost exactly: another +//! complication is that we might want to compile the same target several times +//! (for example, with and without tests), so we actually build a dependency +//! graph of `Unit`s, which capture these properties. + +use ops::Unit; +use std::collections::HashMap; +use CargoResult; +use core::dependency::Kind as DepKind; +use ops::{Context, Kind}; +use core::Target; +use core::Profile; + +pub fn build_unit_dependencies<'a, 'cfg>( + roots: &[Unit<'a>], + cx: &Context<'a, 'cfg>, +) -> CargoResult, Vec>>> { + let mut deps = HashMap::new(); + for unit in roots.iter() { + deps_of(unit, cx, &mut deps)?; + } + + Ok(deps) +} + +fn deps_of<'a, 'b, 'cfg>( + unit: &Unit<'a>, + cx: &Context<'a, 'cfg>, + deps: &'b mut HashMap, Vec>>, +) -> CargoResult<&'b [Unit<'a>]> { + if !deps.contains_key(unit) { + let unit_deps = compute_deps(unit, cx, deps)?; + deps.insert(*unit, unit_deps.clone()); + for unit in unit_deps { + deps_of(&unit, cx, deps)?; + } + } + Ok(deps[unit].as_ref()) +} + +/// For a package, return all targets which are registered as dependencies +/// for that package. +fn compute_deps<'a, 'b, 'cfg>( + unit: &Unit<'a>, + cx: &Context<'a, 'cfg>, + deps: &'b mut HashMap, Vec>>, +) -> CargoResult>> { + if unit.profile.run_custom_build { + return compute_deps_custom_build(unit, cx, deps); + } else if unit.profile.doc && !unit.profile.test { + return compute_deps_doc(unit, cx); + } + + let id = unit.pkg.package_id(); + let deps = cx.resolve.deps(id); + let mut ret = deps.filter(|dep| { + unit.pkg + .dependencies() + .iter() + .filter(|d| d.name() == dep.name() && d.version_req().matches(dep.version())) + .any(|d| { + // If this target is a build command, then we only want build + // dependencies, otherwise we want everything *other than* build + // dependencies. + if unit.target.is_custom_build() != d.is_build() { + return false; + } + + // If this dependency is *not* a transitive dependency, then it + // only applies to test/example targets + if !d.is_transitive() && !unit.target.is_test() && !unit.target.is_example() + && !unit.profile.test + { + return false; + } + + // If this dependency is only available for certain platforms, + // make sure we're only enabling it for that platform. + if !cx.dep_platform_activated(d, unit.kind) { + return false; + } + + // If the dependency is optional, then we're only activating it + // if the corresponding feature was activated + if d.is_optional() && !cx.resolve.features(id).contains(&*d.name()) { + return false; + } + + // If we've gotten past all that, then this dependency is + // actually used! + true + }) + }).filter_map(|id| match cx.get_package(id) { + Ok(pkg) => pkg.targets().iter().find(|t| t.is_lib()).map(|t| { + let unit = Unit { + pkg, + target: t, + profile: lib_or_check_profile(unit, t, cx), + kind: unit.kind.for_target(t), + }; + Ok(unit) + }), + Err(e) => Some(Err(e)), + }) + .collect::>>()?; + + // If this target is a build script, then what we've collected so far is + // all we need. If this isn't a build script, then it depends on the + // build script if there is one. + if unit.target.is_custom_build() { + return Ok(ret); + } + ret.extend(dep_build_script(unit, cx)); + + // If this target is a binary, test, example, etc, then it depends on + // the library of the same package. The call to `resolve.deps` above + // didn't include `pkg` in the return values, so we need to special case + // it here and see if we need to push `(pkg, pkg_lib_target)`. + if unit.target.is_lib() && !unit.profile.doc { + return Ok(ret); + } + ret.extend(maybe_lib(unit, cx)); + + // Integration tests/benchmarks require binaries to be built + if unit.profile.test && (unit.target.is_test() || unit.target.is_bench()) { + ret.extend( + unit.pkg + .targets() + .iter() + .filter(|t| { + let no_required_features = Vec::new(); + + t.is_bin() && + // Skip binaries with required features that have not been selected. + t.required_features().unwrap_or(&no_required_features).iter().all(|f| { + cx.resolve.features(id).contains(f) + }) + }) + .map(|t| Unit { + pkg: unit.pkg, + target: t, + profile: lib_or_check_profile(unit, t, cx), + kind: unit.kind.for_target(t), + }), + ); + } + Ok(ret) +} + +/// Returns the dependencies needed to run a build script. +/// +/// The `unit` provided must represent an execution of a build script, and +/// the returned set of units must all be run before `unit` is run. +fn compute_deps_custom_build<'a, 'cfg>( + unit: &Unit<'a>, + cx: &Context<'a, 'cfg>, + deps: &mut HashMap, Vec>>, +) -> CargoResult>> { + // When not overridden, then the dependencies to run a build script are: + // + // 1. Compiling the build script itcx + // 2. For each immediate dependency of our package which has a `links` + // key, the execution of that build script. + let not_custom_build = unit.pkg + .targets() + .iter() + .find(|t| !t.is_custom_build()) + .unwrap(); + let tmp = Unit { + target: not_custom_build, + profile: &cx.profiles.dev, + ..*unit + }; + let deps = deps_of(&tmp, cx, deps)?; + Ok(deps.iter() + .filter_map(|unit| { + if !unit.target.linkable() || unit.pkg.manifest().links().is_none() { + return None; + } + dep_build_script(unit, cx) + }) + .chain(Some(Unit { + profile: cx.build_script_profile(unit.pkg.package_id()), + kind: Kind::Host, // build scripts always compiled for the host + ..*unit + })) + .collect()) +} + +/// Returns the dependencies necessary to document a package +fn compute_deps_doc<'a, 'cfg>( + unit: &Unit<'a>, + cx: &Context<'a, 'cfg>, +) -> CargoResult>> { + let deps = cx.resolve + .deps(unit.pkg.package_id()) + .filter(|dep| { + unit.pkg + .dependencies() + .iter() + .filter(|d| d.name() == dep.name()) + .any(|dep| match dep.kind() { + DepKind::Normal => cx.dep_platform_activated(dep, unit.kind), + _ => false, + }) + }) + .map(|dep| cx.get_package(dep)); + + // To document a library, we depend on dependencies actually being + // built. If we're documenting *all* libraries, then we also depend on + // the documentation of the library being built. + let mut ret = Vec::new(); + for dep in deps { + let dep = dep?; + let lib = match dep.targets().iter().find(|t| t.is_lib()) { + Some(lib) => lib, + None => continue, + }; + ret.push(Unit { + pkg: dep, + target: lib, + profile: lib_or_check_profile(unit, lib, cx), + kind: unit.kind.for_target(lib), + }); + if cx.build_config.doc_all { + ret.push(Unit { + pkg: dep, + target: lib, + profile: &cx.profiles.doc, + kind: unit.kind.for_target(lib), + }); + } + } + + // Be sure to build/run the build script for documented libraries as + ret.extend(dep_build_script(unit, cx)); + + // If we document a binary, we need the library available + if unit.target.is_bin() { + ret.extend(maybe_lib(unit, cx)); + } + Ok(ret) +} + +fn maybe_lib<'a, 'cfg>(unit: &Unit<'a>, cx: &Context<'a, 'cfg>) -> Option> { + unit.pkg + .targets() + .iter() + .find(|t| t.linkable()) + .map(|t| Unit { + pkg: unit.pkg, + target: t, + profile: lib_or_check_profile(unit, t, cx), + kind: unit.kind.for_target(t), + }) +} + +/// If a build script is scheduled to be run for the package specified by +/// `unit`, this function will return the unit to run that build script. +/// +/// Overriding a build script simply means that the running of the build +/// script itself doesn't have any dependencies, so even in that case a unit +/// of work is still returned. `None` is only returned if the package has no +/// build script. +fn dep_build_script<'a, 'cfg>(unit: &Unit<'a>, cx: &Context<'a, 'cfg>) -> Option> { + unit.pkg + .targets() + .iter() + .find(|t| t.is_custom_build()) + .map(|t| Unit { + pkg: unit.pkg, + target: t, + profile: &cx.profiles.custom_build, + kind: unit.kind, + }) +} + +fn lib_or_check_profile<'a, 'cfg>( + unit: &Unit, + target: &Target, + cx: &Context<'a, 'cfg>, +) -> &'a Profile { + if !target.is_custom_build() && !target.for_host() + && (unit.profile.check || (unit.profile.doc && !unit.profile.test)) + { + return &cx.profiles.check; + } + cx.lib_profile() +} diff --git a/src/cargo/ops/cargo_rustc/custom_build.rs b/src/cargo/ops/cargo_rustc/custom_build.rs index 32fb3ef74..ce0c0efd2 100644 --- a/src/cargo/ops/cargo_rustc/custom_build.rs +++ b/src/cargo/ops/cargo_rustc/custom_build.rs @@ -101,7 +101,8 @@ pub fn prepare<'a, 'cfg>( } fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<(Work, Work)> { - let dependencies = cx.dep_run_custom_build(unit)?; + assert!(unit.profile.run_custom_build); + let dependencies = cx.dep_targets(unit); let build_script_unit = dependencies .iter() .find(|d| !d.profile.run_custom_build && d.target.is_custom_build()) @@ -581,7 +582,7 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca // to rustc invocation caching schemes, so be sure to generate the same // set of build script dependency orderings via sorting the targets that // come out of the `Context`. - let mut targets = cx.dep_targets(unit)?; + let mut targets = cx.dep_targets(unit); targets.sort_by_key(|u| u.pkg.package_id()); for unit in targets.iter() { diff --git a/src/cargo/ops/cargo_rustc/fingerprint.rs b/src/cargo/ops/cargo_rustc/fingerprint.rs index 38ac975de..bd441d358 100644 --- a/src/cargo/ops/cargo_rustc/fingerprint.rs +++ b/src/cargo/ops/cargo_rustc/fingerprint.rs @@ -431,7 +431,7 @@ fn calculate<'a, 'cfg>( // elsewhere. Also skip fingerprints of binaries because they don't actually // induce a recompile, they're just dependencies in the sense that they need // to be built. - let deps = cx.dep_targets(unit)?; + let deps = cx.dep_targets(unit); let deps = deps.iter() .filter(|u| !u.target.is_custom_build() && !u.target.is_bin()) .map(|unit| { diff --git a/src/cargo/ops/cargo_rustc/job_queue.rs b/src/cargo/ops/cargo_rustc/job_queue.rs index c71bab90e..cf4c93625 100644 --- a/src/cargo/ops/cargo_rustc/job_queue.rs +++ b/src/cargo/ops/cargo_rustc/job_queue.rs @@ -416,7 +416,7 @@ impl<'a> Key<'a> { profile: self.profile, kind: self.kind, }; - let targets = cx.dep_targets(&unit)?; + let targets = cx.dep_targets(&unit); Ok(targets .iter() .filter_map(|unit| { diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index a602c7c84..695b882c3 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -170,6 +170,7 @@ pub fn compile_targets<'a, 'cfg: 'a>( cx.prepare()?; cx.probe_target_info()?; + cx.build_unit_dependencies(&units)?; cx.build_used_in_plugin_map(&units)?; custom_build::build_map(&mut cx, &units)?; @@ -215,7 +216,7 @@ pub fn compile_targets<'a, 'cfg: 'a>( } } - for dep in cx.dep_targets(unit)?.iter() { + for dep in cx.dep_targets(unit).iter() { if !unit.target.is_lib() { continue; } @@ -333,7 +334,7 @@ fn compile<'a, 'cfg: 'a>( drop(p); // Be sure to compile all dependencies of this target as well. - for unit in cx.dep_targets(unit)?.iter() { + for unit in cx.dep_targets(unit).iter() { compile(cx, jobs, unit, exec)?; } @@ -1022,7 +1023,7 @@ fn build_deps_args<'a, 'cfg>( }); } - let dep_targets = cx.dep_targets(unit)?; + let dep_targets = cx.dep_targets(unit); // If there is not one linkable target but should, rustc fails later // on if there is an `extern crate` for it. This may turn into a hard diff --git a/src/cargo/ops/cargo_rustc/output_depinfo.rs b/src/cargo/ops/cargo_rustc/output_depinfo.rs index 7508ca9d5..a8a83425e 100644 --- a/src/cargo/ops/cargo_rustc/output_depinfo.rs +++ b/src/cargo/ops/cargo_rustc/output_depinfo.rs @@ -61,7 +61,7 @@ fn add_deps_for_unit<'a, 'b>( } // Recursively traverse all transitive dependencies - for dep_unit in &context.dep_targets(unit)? { + for dep_unit in context.dep_targets(unit).iter() { let source_id = dep_unit.pkg.package_id().source_id(); if source_id.is_path() { add_deps_for_unit(deps, context, dep_unit, visited)?; -- 2.30.2